/*
*    Author : heiden deng(dengjianquan@beyondcent.com)
*    2017/07/19
*    version 0.0.9
*    黑白名单处理类定义
*/


#include "ConditionRouter.h"
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <string>
#include <map>
#include <sstream>

std::string& grpc_trim(std::string &s)
{
	if (s.empty())
	{
		return s;
	}

	s.erase(0, s.find_first_not_of(" "));
	s.erase(s.find_last_not_of(" ") + 1);
	return s;
}

void int2str(const int &int_temp, string &string_temp)
{
	stringstream stream;
	stream << int_temp;
	string_temp = stream.str();   //此处也可以用 stream>>string_temp  
}

int grpc_split_to_vec(const std::string& str, std::vector<string>& ret_, std::string sep = ",")
{
	if (str.empty())
	{
		return 0;
	}

	std::string tmp;
	std::string::size_type pos_begin = str.find_first_not_of(sep);
	std::string::size_type comma_pos = 0;

	while (pos_begin != string::npos)
	{
		comma_pos = str.find(sep, pos_begin);
		if (comma_pos != string::npos)
		{
			tmp = str.substr(pos_begin, comma_pos - pos_begin);
			pos_begin = comma_pos + sep.length();
		}
		else
		{
			tmp = str.substr(pos_begin);
			pos_begin = comma_pos;
		}

		if (!tmp.empty())
		{
			ret_.push_back(tmp);
			tmp.clear();
		}
	}
	return 0;
}

int grpc_split_to_set(const std::string& str, std::set<string>& ret_, std::string sep = ",")
{
	if (str.empty())
	{
		return 0;
	}

	std::string tmp;
	std::string::size_type pos_begin = str.find_first_not_of(sep);
	std::string::size_type comma_pos = 0;

	while (pos_begin != string::npos)
	{
		comma_pos = str.find(sep, pos_begin);
		if (comma_pos != string::npos)
		{
			tmp = str.substr(pos_begin, comma_pos - pos_begin);
			pos_begin = comma_pos + sep.length();
		}
		else
		{
			tmp = str.substr(pos_begin);
			pos_begin = comma_pos;
		}

		if (!tmp.empty())
		{
			ret_.insert(tmp);
			tmp.clear();
		}
	}
	return 0;
}


std::map<std::string, std::string> url_to_map(url_t *url) {
	std::map<std::string, std::string> result;
	if (url->protocol)
	{
		result.insert(std::pair<std::string, std::string>("protocol",url->protocol));
	}
	if (url->username)
	{
		result.insert(std::pair<std::string, std::string>("username", url->username));
	}
	if (url->password)
	{
		result.insert(std::pair<std::string, std::string>("password", url->password));
	}
	if (url->host)
	{
		result.insert(std::pair<std::string, std::string>("host", url->host));
	}

	if (url->port > 0)
	{
		std::string portStr;
		int2str(url->port, portStr);
		result.insert(std::pair<std::string, std::string>("port", portStr));
	}
	if (url->path)
	{
		result.insert(std::pair<std::string, std::string>("path", url->path));
	}
	for (size_t i = 0; i < url->params_num; i++)
	{
		result.insert(std::pair<std::string, std::string>(url->parameters[i].key, url->parameters[i].value));
	}
	return result;
}

ConditionRouter::ConditionRouter(url_t* url_param) {
	char *p = NULL;
	url = (url_t*)gpr_zalloc(sizeof(url_t));
	url_init(url);
	url_clone(url_param, url);
	p = url_get_parameter_v2(url, HTSC_GRPC_REGISTRY_KEY_PRIORITY, NULL);
	priority = atoi(p);
	p = url_get_parameter_v2(url, HTSC_GRPC_REGISTRY_KEY_FORCE, NULL);
	force = false;
	if (0 == strcmp(p,"true"))
	{
		force = true;
	}
	p = url_get_paramter_decode(url, HTSC_GRPC_REGISTRY_KEY_RULE, NULL);
	if (!p)
	{
		gpr_log(GPR_ERROR, "Illegal route rule!");
		return;
	}
	std::string rule = p;
	FREE_PTR(p);
	std::string consumer_prefix = "consumer.";
	std::string provider_prefix = "provider.";
	string::size_type   pos(0);
	//删除consumer.以及provider.前缀
	pos = rule.find(consumer_prefix);
	if (pos != std::string::npos)
	{
		rule = rule.replace(pos, consumer_prefix.length(), "");
	}
	pos = rule.find(provider_prefix);
	if (pos != std::string::npos)
	{
		rule = rule.replace(pos, provider_prefix.length(), "");
	}
	string::size_type i = rule.find("=>");
	std::string whenRule = (i == std::string::npos) ? "" : rule.substr(0, i);
	std::string thenRule = (i == std::string::npos) ? rule: rule.substr(i + 2);
	grpc_trim(whenRule);
	grpc_trim(thenRule);
	if (!whenRule.empty() && 0 != strcmp(whenRule.c_str(),"true"))
	{
		whenCondition = parseRule(whenRule);
	}
	if (!thenRule.empty() && 0 != strcmp(thenRule.c_str(), "false"))
	{
		thenCondition = parseRule(thenRule);
	}

}
std::map<std::string, ConditionRouter::MatchPair> ConditionRouter::parseRule(std::string rule) {
	std::map<string, ConditionRouter::MatchPair> condition;
	if (rule.empty())
	{
		return condition;
	}
	std::map<string, ConditionRouter::MatchPair>::iterator condIter;
	std::vector<std::string> buf_vec;
	string::size_type pos_begin;
	std::string rule_key, rule_value;
	std::string no_equal_str = "!=";
	std::string equal_str = "=";
	grpc_split_to_vec(rule, buf_vec, "&");//处理多个key=value的情形
	for (size_t i = 0; i < buf_vec.size(); i++)
	{
		std::string &item = buf_vec[i];
		pos_begin = item.find_first_of(no_equal_str);//先处理不等于逻辑，即白名单
		
		if (pos_begin != std::string::npos)
		{
			rule_key = item.substr(0, pos_begin);
			rule_value = item.substr(pos_begin + no_equal_str.length());
			grpc_trim(rule_key);
			grpc_trim(rule_value);
			condIter = condition.find(rule_key);
			if (condIter != condition.end())
			{
				MatchPair &pair = condIter->second;
				std::set<std::string> &values = pair.mismatches;
				grpc_split_to_set(rule_value, values, ",");
			}
			else {
				MatchPair pair;
				std::set<std::string> &values = pair.mismatches;
				grpc_split_to_set(rule_value, values, ",");
				condition.insert(std::pair<std::string, MatchPair>(rule_key, pair));
			}
		}
		else {
			pos_begin = item.find_first_of(equal_str);
			if (pos_begin != std::string::npos)
			{
				rule_key = item.substr(0, pos_begin);
				rule_value = item.substr(pos_begin + equal_str.length());
				grpc_trim(rule_key);
				grpc_trim(rule_value);
				condIter = condition.find(rule_key);
				if (condIter != condition.end())
				{
					MatchPair &pair = condIter->second;
					std::set<std::string> &values = pair.matches;
					grpc_split_to_set(rule_value, values, ",");
				}
				else {
					MatchPair pair;
					std::set<std::string> &values = pair.matches;
					grpc_split_to_set(rule_value, values, ",");
					condition.insert(std::pair<std::string, MatchPair>(rule_key, pair));
				}
			}
			else {
				gpr_log(GPR_ERROR, "Unsupport rule. rule:%s", item.c_str());
				continue;
			}
		}
	}
	return condition;
}
int ConditionRouter::route(std::vector<provider_t*> &providers, url_t *url_param) {
	if (0 == providers.size())
	{
		return 0;
	}
	if (!matchWhen(url_param))
	{
		return 0;
	}
	int valid_provider_num = 0;;
	if (0 == thenCondition.size())
	{
		gpr_log(GPR_ERROR, "The current consumer in the service blacklist. consumer:%s",url_param->host);
		return -1;
	}
	std::vector<provider_t*>::iterator providersIter;
	std::vector<int> provider_pos;
	for (size_t i = 0; i < providers.size(); i++)
	{
		if (HTSC_GRPC_CHECK_BIT(providers[i]->flag, HTSC_GRPC_PROVIDER_FLAG_IN_BLKLIST))
		{
			continue;
		}
		if ( matchThen((url_t*)providers[i]->ext_data , url_param))
		{		
			valid_provider_num++;
		}
		else {
			HTSC_GRPC_SET_BIT(providers[i]->flag, HTSC_GRPC_PROVIDER_FLAG_IN_BLKLIST);//设置黑名单标记
			provider_pos.push_back(i);
		}
	}
	if (valid_provider_num > 0)
	{
		return 1;
	}
	else if (force)
	{
		gpr_log(GPR_ERROR, "The route result is empty and force execute. consumer:%s", url_param->host);
		return -1;
	}
	for (size_t i = 0; i < provider_pos.size(); i++)
	{
		HTSC_GRPC_CLR_BIT(providers[provider_pos[i]]->flag, HTSC_GRPC_PROVIDER_FLAG_IN_BLKLIST);//清除黑名单标记
	}
	return 0;
}
ConditionRouter::~ConditionRouter() {
	if (url)
	{
		url_full_free(&url);
	}
}

bool ConditionRouter::matchWhen(url_t* url) {
	if (whenCondition.size() == 0) {
		return true;// 如果匹配条件为空，表示对所有消费方应用
	}
	return matchCondition(whenCondition, url, NULL);
}

bool ConditionRouter::matchThen(url_t* url, url_t* param) {
	return matchCondition(thenCondition, url, param);
}

bool ConditionRouter::matchCondition(std::map<std::string, MatchPair> condition, url_t* url, url_t* param) {
	std::map<std::string, std::string> sample = url_to_map(url);
	std::map<std::string, std::string>::iterator sampIter;
	std::map<std::string, MatchPair>::iterator condIter;
	for (sampIter = sample.begin();sampIter != sample.end(); sampIter++) {
		//String key = entry.getKey();
		condIter = condition.find(sampIter->first);
		if (condIter != condition.end())
		{
			MatchPair &pair = condIter->second;
			if (!pair.isMatch(sampIter->second, param)) {
				return false;
			}
		}
	}
	return true;
}